package com.yox.rpc.factory;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.*;

/**
 * 创建线程池工具类 (ThreadPoll)
 *
 * 将Socket中使用到的线程池封装，使用guava中的ThreadFactoryBuilder()自定义线程名，方便出错时定位
 */
public class ThreadPoolFactory {
    /**
     * 线程池参数
     */
    private static final int CORE_POOL_SIZE = 10;
    private static final int MAXIMUM_POOL_SIZE = 100;
    private static final int KEEP_ALIVE_TIME = 1;
    private static final int BLOCKING_QUEUE_CAPACITY = 100;

    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolFactory.class);
    /**
     * 利用Map管理多个线程池,线程池前缀名作为Key，线程为Value，ConcurrentHashMap保证线程安全
     */
    private static Map<String, ExecutorService> threadPoolMap = new ConcurrentHashMap<>();

    private ThreadPoolFactory() {
    }

    public static ExecutorService createDefaultThreadPool(String threadNamePrefix, Boolean daemon) {
        // computeIfAbsent()：如果key对应的value存在，则直接返回value，如果不存在则使用第二个参数（函数）计算的值作为value返回，并保存为该key的value
        ExecutorService pool = threadPoolMap.computeIfAbsent(threadNamePrefix, k -> createThreadPool(threadNamePrefix, daemon));
        // isShutdown()：当调用shutdown()或shutdownNow()方法后返回为true
        // isTerminated()：当调用shutdown()方法后，并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后，成功停止后返回为true;
        if(pool.isShutdown() || pool.isTerminated()){
            threadPoolMap.remove(threadNamePrefix);
            // 重新构建一个线程池并存入Map中
            pool = createThreadPool(threadNamePrefix, daemon);
            // 将新建的线程池放进Map管理
            threadPoolMap.put(threadNamePrefix, pool);
        }
        return pool;
    }

    /**
     * 关闭所有线程池的shutDownAll()方法也在ShutdownHook钩子中调用，当服务端关闭的时候，会注销所有服务，同时关闭所有线程池。
     */
    public static void shutDownAll(){
        logger.info("关闭所有线程池……");
        // 利用parallelStream()并行关闭所有线程池
        threadPoolMap.entrySet().parallelStream().forEach(entry -> {
            ExecutorService executorService = entry.getValue();
            executorService.shutdown();
            logger.info("关闭线程池 [{}] [{}]", entry.getKey(), executorService.isTerminated());
            try {
                // 阻塞直到关闭请求后所有任务执行完，或者发生超时，或者当前线程被中断（以先发生者为准）。
                executorService.awaitTermination(10, TimeUnit.SECONDS);
            }catch (InterruptedException e){
                logger.error("关闭线程池失败");
                // 直接关闭不等任务执行完了
                executorService.shutdownNow();
            }
        });
    }

    // 创建一个线程池
    private static ExecutorService createThreadPool(String threadNamePrefix, Boolean daemon){
        // 设置上限为100个线程的阻塞队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY);
        // 调用线程池工具类
        ThreadFactory threadFactory = createThreadFactory(threadNamePrefix, daemon);
        return new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MINUTES, workQueue, threadFactory);
    }

    public static ExecutorService createDefaultThreadPool(String threadNamePrefix) {
        return createDefaultThreadPool(threadNamePrefix, false);
    }

    /**
     * 创建 ThreadFactory，如果 threadNamePrefix 不为空，则使用自建 ThreadFactory ，否则使用 defaultThreadPool
     * @param threadNamePrefix 作为创建的线程名字的前缀，指定有意义的线程名称，方便出错时回溯
     * @param daemon 指定是否为守护线程(Daemon Thread)，当所有的非守护线程结束时，程序也就终止了，同时会杀死进程中的所有守护线程
     * @return [java.util.concurrent.ThreadFactory]
     */
    private static ThreadFactory createThreadFactory(String threadNamePrefix, Boolean daemon) {
        if (threadNamePrefix != null) {
            if (daemon != null) {
                // 利用guava中的TreadFactoryBuilder自定义创建线程工厂
                return new ThreadFactoryBuilder()
                        .setNameFormat(threadNamePrefix + "-%d")
                        .setDaemon(daemon)
                        .build();
            } else {
                return new ThreadFactoryBuilder()
                        .setNameFormat(threadNamePrefix + "-%d")
                        .build();
            }
        }
        return Executors.defaultThreadFactory();
    }
}
